home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / daemons / init / sysvinit.000 / sysvinit / sysvinit-2.64 / shutdown.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-02  |  11.8 KB  |  493 lines

  1. /*
  2.  * shutdown.c    Shut the system down.
  3.  *
  4.  * Usage:    shutdown [-krhfnc] time [warning message]
  5.  *          -k: don't really shutdown, only warn.
  6.  *          -r: reboot after shutdown.
  7.  *          -h: halt after shutdown.
  8.  *          -f: do a 'fast' reboot.
  9.  *          -n: do not go through init but do it ourselves.
  10.  *          -c: cancel an already running shutdown.
  11.  *          -t secs: delay between SIGTERM and SIGKILL for init.
  12.  *
  13.  * Author:    Miquel van Smoorenburg, miquels@drinkel.cistron.nl
  14.  *
  15.  * Version:    @(#)shutdown  2.2  04-Jun-1996  MvS
  16.  *
  17.  *        This file is part of the sysvinit suite,
  18.  *        Copyright 1991-1996 Miquel van Smoorenburg.
  19.  *
  20.  *        This program is free software; you can redistribute it and/or
  21.  *        modify it under the terms of the GNU General Public License
  22.  *        as published by the Free Software Foundation; either version
  23.  *        2 of the License, or (at your option) any later version.
  24.  */
  25. #include <sys/types.h>
  26. #include <sys/stat.h>
  27. #include <time.h>
  28. #include <string.h>
  29. #include <unistd.h>
  30. #include <errno.h>
  31. #include <stdlib.h>
  32. #include <stdio.h> 
  33. #include <signal.h>
  34. #include <fcntl.h>
  35. #include <utmp.h>
  36. #include "paths.h"
  37.  
  38. char *Version = "@(#) shutdown 2.2 04-Jun-1996 MvS";
  39.  
  40. #define MESSAGELEN    256
  41.  
  42. int dontshut = 0;    /* Don't shutdown, only warn    */
  43. char down_level[2];    /* What runlevel to go to.    */
  44. int dosync = 1;        /* Sync before reboot or halt    */
  45. int fastboot = 0;    /* Do a 'fast' reboot        */
  46. char message[MESSAGELEN];    /* Warning message    */
  47. char *sltime = 0;    /* Sleep time            */
  48. char newstate[64];    /* What are we gonna do        */
  49. int doself = 0;        /* Don't use init        */
  50.  
  51. /* From "wall.c" */
  52. extern void wall();
  53.  
  54. /*
  55.  * Break off an already running shutdown.
  56.  */
  57. void stopit()
  58. {
  59.   unlink(NOLOGIN);
  60.   unlink(FASTBOOT);
  61.   unlink(SDPID);
  62.   printf("\r\nShutdown cancelled.\r\n");
  63.   exit(0);
  64. }
  65.  
  66. /*
  67.  * Show usage message.
  68.  */
  69. void usage()
  70. {
  71.  fprintf(stderr,
  72.     "Usage:\t  shutdown [-krhfnc] [-t secs] time [warning message]\n"
  73.     "\t\t  -k:      don't really shutdown, only warn.\n"
  74.     "\t\t  -r:      reboot after shutdown.\n"
  75.     "\t\t  -h:      halt after shutdown.\n"
  76.     "\t\t  -f:      do a 'fast' reboot.\n"
  77.     "\t\t  -n:      do not go through \"init\" but go down real fast.\n"
  78.     "\t\t  -c:      cancel a running shutdown.\n"
  79.     "\t\t  -t secs: delay between warning and kill signal.\n"
  80.     "\t\t  ** the \"time\" argument is mandatory! (try \"now\") **\n");
  81.   exit(1);
  82. }
  83.  
  84. /*
  85.  * Tell everyone the system is going down in 'mins' minutes.
  86.  */
  87. void warn(mins)
  88. int mins;
  89. {
  90.   char buf[MESSAGELEN + 64];
  91.   int len;
  92.  
  93.   strcpy(buf, message);
  94.   len = strlen(buf);
  95.  
  96.   if (mins == 0)
  97.     sprintf(buf + len, "\rThe system is going down %s NOW !!\r\n", newstate);
  98.   else
  99.       sprintf(buf + len,
  100.         "\rThe system is going DOWN %s in %d minutes !!\r\n", newstate, mins);
  101.   wall(buf, 1);
  102. }
  103.  
  104. /*
  105.  * Create the /etc/nologin file.
  106.  */
  107. void donologin()
  108. {
  109.   FILE *fp;
  110.   time_t t;
  111.  
  112.   time(&t);
  113.   t += 300;
  114.  
  115.   unlink(NOLOGIN);
  116.   if ((fp = fopen(NOLOGIN, "w")) != NULL) {
  117.       fprintf(fp, "\rThe system is going down on %s\r\n", ctime(&t));
  118.       if (message[0]) fputs(message, fp);
  119.       fclose(fp);
  120.   }
  121. }
  122.  
  123. /* Kill all processes, call /etc/init.d/halt (if present) */
  124. void fastdown()
  125. {
  126.   int do_halt = (down_level[0] == '0');
  127.   char *script;
  128.   char cmd[128];
  129.   time_t t;
  130.   int fd, i;
  131.   struct utmp wtmp;
  132.  
  133.   /* Currently, the halt script is either init.d/halt OR rc.d/rc.0,
  134.    * likewise for the reboot script. Test for the presence
  135.    * of either.
  136.    */
  137.   if (do_halt) {
  138.     if (access(HALTSCRIPT1, X_OK) == 0)
  139.         script = HALTSCRIPT1;
  140.     else
  141.         script = HALTSCRIPT2;
  142.   } else {
  143.     if (access(REBOOTSCRIPT1, X_OK) == 0)
  144.         script = REBOOTSCRIPT1;
  145.     else
  146.         script = REBOOTSCRIPT2;
  147.   }
  148.  
  149.   /* First close all files. */
  150.   for(i = 0; i < 3; i++) if (!isatty(i)) close(i);
  151.   for(i = 3; i < 20; i++) close(i);
  152.   close(255);
  153.  
  154.   /* First idle init. */
  155.   if (kill(1, SIGTSTP) < 0) {
  156.     fprintf(stderr, "shutdown: can't idle init.\r\n");
  157.     exit(1);
  158.   }
  159.  
  160.   /* Kill all processes. */
  161.   fprintf(stderr, "shutdown: sending all processes the TERM signal...\r\n");
  162.   (void) kill(-1, SIGTERM);
  163.   if (sltime)
  164.     sleep(atoi(sltime));
  165.   else
  166.     sleep(3);
  167.   fprintf(stderr, "shutdown: sending all processes the KILL signal.\r\n");
  168.   (void) kill(-1, SIGKILL);
  169.  
  170.   /* See if we can run /etc/init.d/halt */
  171.   if (access(script, X_OK) == 0) {
  172.     sprintf(cmd, "%s fast", script);
  173.     system(cmd);
  174.     fprintf(stderr, "shutdown: %s returned - falling back on default routines\r\n", script);
  175.   }
  176.  
  177.   /* script failed or not present: do it ourself. */
  178.   sleep(1); /* Give init the chance to collect zombies. */
  179.   setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin", 1);
  180.  
  181.   /* Record the fact that we're going down */
  182.   if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND)) >= 0) {
  183.         time(&t);
  184.         strcpy(wtmp.ut_user, "shutdown");
  185.         strcpy(wtmp.ut_line, "~");
  186.         strcpy(wtmp.ut_id,  "~~");
  187.         wtmp.ut_pid = 0;
  188.         wtmp.ut_type = RUN_LVL;
  189.         wtmp.ut_time = t;
  190.         write(fd, (char *)&wtmp, sizeof(wtmp));
  191.         close(fd);
  192.   }
  193.  
  194.   /* This is for those who have quota installed. */
  195.   system("accton 2>/dev/null");
  196.   system("quotaoff -a 2>/dev/null");
  197.  
  198.   fprintf(stderr, "shutdown: turning off swap\r\n");
  199.   system("swapoff -a");
  200.   fprintf(stderr, "shutdown: unmounting all file systems\r\n");
  201.   system("umount -a");
  202.  
  203.   /* We're done, halt or reboot now. */
  204.   if (do_halt) {
  205.     fprintf(stderr, "The system is halted. Press CTRL-ALT-DEL or turn off power\r\n");
  206.     reboot(0xfee1dead, 672274793, 0x89ABCDEF);
  207.     exit(0);
  208.   }
  209.   fprintf(stderr, "Please stand by while rebooting the system.\r\n");
  210.   reboot(0xfee1dead, 672274793, 0x01234567);
  211.   exit(0);
  212. }
  213.  
  214. /*
  215.  * Go to runlevel 0, 1 or 6.
  216.  */
  217. void shutdown()
  218. {
  219.   char *args[8];
  220.   int argp = 0;
  221.  
  222.   /* Warn for the last time (hehe) */
  223.   warn(0);
  224.   if (dontshut) stopit();
  225.  
  226.   /* See if we have to do it ourself. */
  227.   if (doself) fastdown();
  228.  
  229.   /* Create the arguments for init. */
  230.   args[argp++] = INIT;
  231.   if (sltime) {
  232.     args[argp++] = "-t";
  233.     args[argp++] = sltime;
  234.   }
  235.   args[argp++] = down_level;
  236.   args[argp]   = (char *)NULL;
  237.  
  238.   unlink(SDPID);
  239.  
  240.   /* Now execute init to change runlevel. */
  241.   execv(INIT, args);
  242.  
  243.   /* Oops - failed. */
  244.   fprintf(stderr, "\rshutdown: cannot execute %s\r\n", INIT);
  245.   unlink(FASTBOOT);
  246.   unlink(NOLOGIN);
  247.   exit(1);
  248. }
  249.  
  250. /*
  251.  * Main program.
  252.  * Process the options and do the final countdown.
  253.  */
  254. int main(argc, argv)
  255. int argc;
  256. char **argv;
  257. {
  258.   extern int getopt();
  259.   extern int optind; 
  260.   int c, i, wt, hours, mins;
  261.   struct tm *lt;
  262.   time_t t;
  263.   char *sp;
  264.   char *when = NULL;
  265.   int didnolog = 0;
  266.   int cancel = 0;
  267.   int pid = 0;
  268.   FILE *fp;
  269.   char *downusers[32];
  270.   char buf[128];
  271.   struct stat st;
  272.   struct utmp *ut;
  273.   int user_ok = 0;
  274.  
  275.   /* We can be installed setuid root (executable for a special group) */
  276.   setuid(geteuid());
  277.  
  278.   if (getuid() != 0) {
  279.       fprintf(stderr, "shutdown: must be root.\n");
  280.       exit(1);
  281.   }
  282.   strcpy(down_level, "1");
  283.  
  284.   /* See if we have a controlling terminal. */
  285.   if (tcgetpgrp(0) != getpgrp() && getppid() == 1 &&
  286.       (fp = fopen(SDALLOW, "r")) != NULL) {
  287.  
  288.     /* No controlling terminal, we were called from init. */
  289.     i = 0;
  290.     /* Read /etc/shutdown.allow. */
  291.     while(fgets(buf, 128, fp)) {
  292.         if (buf[0] == '#' || buf[0] == '\n') continue;
  293.         if (i > 31) continue;
  294.         for(sp = buf; *sp; sp++) if (*sp == '\n') *sp = 0;
  295.         downusers[i++] = strdup(buf);
  296.     }
  297.     if (i < 32) downusers[i] = 0;
  298.     fclose(fp);
  299.  
  300.     /* Now walk through /var/run/utmp to find logged in users. */
  301.     while(!user_ok && (ut = getutent()) != NULL) {
  302.  
  303.         /* See if this is a user process on a VC. */
  304.         if (ut->ut_type != USER_PROCESS) continue;
  305.         sprintf(buf, "/dev/%s", ut->ut_line);
  306.         if (stat(buf, &st) < 0) continue;
  307.         if ((st.st_rdev & 0xFFC0) != 0x0400) continue;
  308.  
  309.         /* Root is always OK. */
  310.         if (strcmp(ut->ut_user, "root") == 0) {
  311.             user_ok++;
  312.             break;
  313.         }
  314.  
  315.         /* See if this is an allowed user. */
  316.         for(i = 0; i < 32 && downusers[i]; i++)
  317.             if (!strncmp(downusers[i], ut->ut_user, UT_NAMESIZE)) {
  318.                 user_ok++;
  319.                 break;
  320.             }
  321.     }
  322.     endutent();
  323.  
  324.     /* See if user was allowed. */
  325.     if (!user_ok) {
  326.         if ((fp = fopen(CONSOLE, "w")) != NULL) {
  327.            fprintf(fp, "\rshutdown: no authorized users logged in.\r\n");
  328.            fclose(fp);
  329.         }
  330.         exit(1);
  331.     }
  332.   }
  333.  
  334.   /* Process the options. */
  335.   while((c = getopt(argc, argv, "cqkrhnfyt:g:i:")) != EOF) {
  336.       switch(c) {
  337.         case 'c': /* Cancel an already running shutdown. */
  338.             cancel = 1;
  339.             break;
  340.           case 'k': /* Don't really shutdown, only warn.*/
  341.               dontshut = 1;
  342.               break;
  343.           case 'r': /* Automatic reboot */
  344.             down_level[0] = '6';
  345.               break;
  346.           case 'h': /* Halt after shutdown */
  347.             down_level[0] = '0';
  348.               break;
  349.           case 'f': /* Don't perform fsck after next boot */
  350.               fastboot = 1;
  351.               break;
  352.         case 'n': /* Don't switch runlevels. */
  353.             doself = 1;
  354.             break;
  355.         case 't': /* Delay between TERM and KILL */
  356.             sltime = optarg;
  357.             break;
  358.         case 'y': /* Ignored for sysV compatibility */
  359.             break;
  360.         case 'g': /* sysv style to specify time. */
  361.             when = optarg;
  362.             down_level[0] = '0';
  363.             break;
  364.         case 'i': /* Level to go to. */
  365.             if (!strchr("0156aAbBcCsS", optarg[0])) {
  366.                 fprintf(stderr, "shutdown: `%s': bad runlevel\n",
  367.                     optarg);
  368.                 exit(1);
  369.             }
  370.             down_level[0] = optarg[0];
  371.             break;
  372.           default:
  373.               usage();
  374.               break;    
  375.       }
  376.   }
  377.   /* Read pid of running shutdown from a file */
  378.   if ((fp = fopen(SDPID, "r")) != NULL) {
  379.     fscanf(fp, "%d", &pid);
  380.     fclose(fp);
  381.   }
  382.  
  383.   /* Read remaining words, skip time if needed. */
  384.   message[0] = 0;
  385.   for(c = optind + (!cancel && !when); c < argc; c++) {
  386.     if (strlen(message) + strlen(argv[c]) + 4 > MESSAGELEN)
  387.         break;
  388.       strcat(message, argv[c]);
  389.       strcat(message, " ");
  390.   }
  391.   if (message[0]) strcat(message, "\r\n");
  392.  
  393.   /* See if we want to run or cancel. */
  394.   if (cancel) {
  395.     if (pid <= 0) {
  396.         fprintf(stderr, "shutdown: cannot find pid of running shutdown.\n");
  397.         exit(1);
  398.     }
  399.     if (kill(pid, SIGINT) < 0) {
  400.         fprintf(stderr, "shutdown: not running.\n");
  401.         exit(1);
  402.     }
  403.     if (message[0]) wall(message, 1);
  404.     exit(0);
  405.   }
  406.   
  407.   /* Check syntax. */
  408.   if (when == NULL) {
  409.     if (optind == argc) usage();
  410.     when = argv[optind++];
  411.   }
  412.  
  413.   /* See if we are already running. */
  414.   if (pid > 0 && kill(pid, 0) == 0) {
  415.     fprintf(stderr, "\rshutdown: already running.\r\n");
  416.     exit(1);
  417.   }
  418.  
  419.   /* Extra check. */
  420.   if (doself && down_level[0] != '0' && down_level[0] != '6') {
  421.     fprintf(stderr, "shutdown: can only use \"-n\" for halt or reboot.\r\n");
  422.     exit(1);
  423.   }
  424.  
  425.   /* Tell users what we're gonna do. */
  426.   switch(down_level[0]) {
  427.     case '0':
  428.         strcpy(newstate, "for system halt");
  429.         break;
  430.     case '6':
  431.         strcpy(newstate, "for reboot");
  432.         break;
  433.     case '1':
  434.         strcpy(newstate, "to maintenance mode");
  435.         break;
  436.     default:
  437.         sprintf(newstate, "to runlevel %s", down_level);
  438.         break;
  439.   }
  440.  
  441.   /* Create a new PID file. */
  442.   unlink(SDPID);
  443.   umask(022);
  444.   if ((fp = fopen(SDPID, "w")) != NULL) {
  445.     fprintf(fp, "%d\n", getpid());
  446.     fclose(fp);
  447.   } else if (errno != EROFS)
  448.     fprintf(stderr, "shutdown: warning: cannot open %s\n", SDPID);
  449.  
  450.   /* Ignore all signals. */
  451.   for(c = 1; c < _NSIG; c++) signal(c, SIG_IGN);
  452.   signal(SIGINT, stopit);
  453.  
  454.   /* Go to the root directory */
  455.   chdir("/");
  456.   if (fastboot) close(open(FASTBOOT, O_CREAT | O_RDWR, 0644));
  457.  
  458.   /* Alias now and take care of old '+mins' notation. */
  459.   if (!strcmp(when, "now")) strcpy(when, "0");
  460.   if (when[0] == '+') when++;
  461.  
  462.   /* Decode shutdown time. */
  463.   if (strchr(when, ':') == NULL) {
  464.     /* Time in minutes. */
  465.       wt = atoi(when);
  466.       if (wt == 0 && when[0] != '0') usage();
  467.   } else {
  468.     /* Time in hh:mm format. */
  469.       if (sscanf(when, "%d:%2d", &hours, &mins) != 2) usage();
  470.       if (hours > 23 || mins > 59) usage();
  471.       time(&t);
  472.       lt = localtime(&t);
  473.       wt = (60*hours + mins) - (60*lt->tm_hour + lt->tm_min);
  474.       if (wt < 0) wt += 1440;
  475.   }
  476.   /* Shutdown NOW if time == 0 */
  477.   if (wt == 0) shutdown();
  478.  
  479.   /* Give warnings on regular intervals and finally shutdown. */
  480.   if (wt < 15 && wt!= 10 && wt != 5 && wt != 1) warn(wt);
  481.   while(wt) {
  482.       if (wt <= 5 && !didnolog) {
  483.           donologin();
  484.           didnolog++;
  485.       }
  486.       if (wt == 15 || wt == 10 || wt == 5 || wt == 1) warn(wt);
  487.       sleep(60);
  488.       wt--;
  489.   }
  490.   shutdown();
  491.   return(0); /* Never happens */
  492. }
  493.